#define MAX_POINTS 32

cbuffer cbPerScene : register( b0 )
{
    row_major matrix g_mProjView    : packoffset( c0 );
};

// output from the vertex shader
struct VS_CONTROL_POINT_OUTPUT
{
    float3 vPosition	: WORLDPOS;
};

// output from the hull shader
struct BEZIER_CONTROL_POINT
{
    float3 vPosition	: BEZIERPOS;
};

// Output patch constant data. (These two are the only things used by the tesselator
struct HS_CONSTANT_DATA_OUTPUT
{
    float Edges[4]        : SV_TessFactor;
    float Inside[2]       : SV_InsideTessFactor;
};

// output from the domain shader
struct DS_OUTPUT
{
    float4 vPosition		: SV_POSITION;
};

// pass along the world position to the hull shader
VS_CONTROL_POINT_OUTPUT BasicVS( float4 Pos : POSITION )
{
    VS_CONTROL_POINT_OUTPUT output = (VS_CONTROL_POINT_OUTPUT)0;

    output.vPosition = Pos;

    return output;
}


// patch constant function
HS_CONSTANT_DATA_OUTPUT SubDToBezierConstantsHS( 
    InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip,
    uint PatchID : SV_PrimitiveID )
{	
    HS_CONSTANT_DATA_OUTPUT Output;

    float TessAmount = 2;
	
    Output.Edges[0] = Output.Edges[1] = Output.Edges[2] = Output.Edges[3] = TessAmount;
    Output.Inside[0] = Output.Inside[1] = TessAmount;
    
    return Output;
}

// hull shader
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(16)]
[patchconstantfunc("SubDToBezierConstantsHS")]
BEZIER_CONTROL_POINT SubDToBezierHS( 
    InputPatch<VS_CONTROL_POINT_OUTPUT, MAX_POINTS> ip, 
    uint i : SV_OutputControlPointID,
    uint PatchID : SV_PrimitiveID )
{
    VS_CONTROL_POINT_OUTPUT Output;

    Output.vPosition = ip[i].vPosition;
    
    return Output;
}


float4 BernsteinBasis(float t)
{
    float invT = 1.0f - t;

    return float4( invT * invT * invT,
                   3.0f * t * invT * invT,
                   3.0f * t * t * invT,
                   t * t * t );
}

float3 EvaluateBezier( const OutputPatch<BEZIER_CONTROL_POINT, 16> bezpatch,
                       float4 BasisU,
                       float4 BasisV )
{
    float3 Value = float3(0,0,0);
    Value  = BasisV.x * ( bezpatch[0].vPosition * BasisU.x + bezpatch[1].vPosition * BasisU.y + bezpatch[2].vPosition * BasisU.z + bezpatch[3].vPosition * BasisU.w );
    Value += BasisV.y * ( bezpatch[4].vPosition * BasisU.x + bezpatch[5].vPosition * BasisU.y + bezpatch[6].vPosition * BasisU.z + bezpatch[7].vPosition * BasisU.w );
    Value += BasisV.z * ( bezpatch[8].vPosition * BasisU.x + bezpatch[9].vPosition * BasisU.y + bezpatch[10].vPosition * BasisU.z + bezpatch[11].vPosition * BasisU.w );
    Value += BasisV.w * ( bezpatch[12].vPosition * BasisU.x + bezpatch[13].vPosition * BasisU.y + bezpatch[14].vPosition * BasisU.z + bezpatch[15].vPosition * BasisU.w );
    
    return Value;
}

// domain shader
[domain("quad")]
DS_OUTPUT BezierEvalDS( HS_CONSTANT_DATA_OUTPUT input, 
                        float2 UV : SV_DomainLocation,
                        const OutputPatch<BEZIER_CONTROL_POINT, 16> bezpatch )
{
    DS_OUTPUT Output;

    float4 BasisU = BernsteinBasis( UV.x );
    float4 BasisV = BernsteinBasis( UV.y );

    float3 WorldPos = EvaluateBezier( bezpatch, BasisU, BasisV );

    Output.vPosition = mul( float4(WorldPos,1), g_mProjView );

    return Output;    
}

